/*
 *  Copyright (c) 2011 Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the GNU Lesser Public License v2.1
 *  which accompanies this distribution, and is available at
 *  http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 *  Contributors:
 *      Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany - initial API and implementation
 */
package de.ipk_gatersleben.bit.bi.edal.primary_data.reference;

import java.io.StringWriter;
import java.util.Calendar;
import javax.xml.bind.JAXBException;

import de.ipk_gatersleben.bit.bi.edal.primary_data.DataManager;
import de.ipk_gatersleben.bit.bi.edal.primary_data.EdalConfiguration;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.EdalException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataEntityException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataEntityVersion;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference;
import de.ipk_gatersleben.bit.bi.edal.primary_data.reference.datacite.DataCiteException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.reference.datacite.DataCiteMDSConnector;
import de.ipk_gatersleben.bit.bi.edal.primary_data.reference.datacite.DataCiteSearchConnector;
import de.ipk_gatersleben.bit.bi.edal.primary_data.reference.datacite.DataCiteXmlMapper;
import de.ipk_gatersleben.bit.bi.edal.primary_data.reference.datacite.XmlFunctions;
import de.ipk_gatersleben.bit.bi.edal.primary_data.reference.datacite.xml.XmlIdentifier;
import de.ipk_gatersleben.bit.bi.edal.primary_data.reference.datacite.xml.XmlResource;

/**
 * DataCite implementation of the {@link EdalReferenceable} interface, to
 * connect the system with the DataCite interface.
 * 
 * @author arendd
 */
public class DataCiteReference implements EdalReferenceable {

	private static final String TEST_URL = "http://doi.ipk-gatersleben.de/testdata/demo_doi_landingpage/";
	private static final String TEST_DOI = EdalConfiguration.DATACITE_TESTPREFIX
			+ "/EDALTEST/" + 12345;

	/** {@inheritDoc} */
	@Override
	public String acceptApprovalRequest(PublicReference publicReference)
			throws EdalApprovalException {

		if (!DataManager.getConfiguration().isInTestMode()) {

			try {
				int year = Calendar.getInstance().get(Calendar.YEAR);

				String doi = this.getPersistentIdentifier(year);

				StringBuffer dataCiteXml = generateDataCiteXML(publicReference,
						doi);

				System.out.println(dataCiteXml);

				System.out.println(doi);
				try {
					System.out.println(publicReference.getVersion().getURL()
							.toString());
				} catch (PrimaryDataEntityException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

				this.validateMetaData(publicReference.getVersion());

				// DataCiteMDSConnector connector = new DataCiteMDSConnector(
				// DataManager.getConfiguration());
				//
				// try {
				// connector.postMetadata(XmlFunctions.parse(dataCiteXml
				// .toString()));
				// connector.postDOI(doi, publicReference.getVersion()
				// .getURL().toString());
				// } catch (DataCiteException | PrimaryDataEntityException e) {
				// throw new EdalApprovalException(
				// "unable to post metadata and DOI to DataCite : ", e);
				// }

				try {
					DataManager.getImplProv().getApprovalServiceProvider()
							.newInstance()
							.storeNewDOI(publicReference, doi, year);
				} catch (InstantiationException | IllegalAccessException e) {
					throw new EdalApprovalException(
							"unable to store a new DOI for the PublicReference");
				}

				return doi;

			} catch (EdalException | EdalPublicationMetaDataException e) {
				throw new EdalApprovalException(
						"unable to accept approvalRequest", e);
			}
		} else {
			try {

				int year = Calendar.getInstance().get(Calendar.YEAR);

				StringBuffer dataCiteXml = generateDataCiteXML(publicReference,
						TEST_DOI);

				this.validateMetaData(publicReference.getVersion());

				System.out.println(dataCiteXml);
				System.out.println(TEST_DOI);

				// DataCiteMDSConnector connector = new DataCiteMDSConnector(
				// DataManager.getConfiguration());
				//
				// try {
				// connector.postMetadata(XmlFunctions.parse(dataCiteXml
				// .toString()));
				// connector.postDOI(TEST_DOI, TEST_URL);
				//
				// } catch (DataCiteException e) {
				// throw new EdalApprovalException(
				// "unable to post metadata and DOI to DataCite: "
				// + e.getMessage(), e);
				// }

				try {
					DataManager.getImplProv().getApprovalServiceProvider()
							.newInstance()
							.storeNewDOI(publicReference, TEST_DOI, year);
				} catch (InstantiationException | IllegalAccessException e) {
					throw new EdalApprovalException(
							"unable to store a new DOI for the PublicReference");
				}

				DataManager
						.getImplProv()
						.getLogger()
						.warn("Your PublicReference was not posted to DataCite, because you are running in Test-Mode");

				return TEST_DOI;

			} catch (EdalException | EdalPublicationMetaDataException e) {
				throw new EdalApprovalException(
						"unable to accept approvalRequest:" + e.getMessage(),
						e.getCause());
			}
		}
	}

	/**
	 * Generate a DataCiteX XML document as {@link String} from a
	 * {@link PublicReference} object.
	 * 
	 * @param publicReference
	 *            the reference to generate a XML document.
	 * @param doi
	 *            the new DOI for this {@link PublicReference}.
	 * @return the XML document as {@link String}.
	 * @throws EdalPublicationMetaDataException
	 *             if unable to marshal the meta data to XML.
	 */
	private StringBuffer generateDataCiteXML(PublicReference publicReference,
			String doi) throws EdalPublicationMetaDataException {

		DataCiteXmlMapper xmlMapper = new DataCiteXmlMapper(
				publicReference.getVersion());

		XmlResource xmlResource = xmlMapper.createXmlResource();

		xmlResource.setIdentifier(new XmlIdentifier(doi));

		StringWriter strw = new StringWriter();

		try {
			xmlMapper.createXmlMarshaller().marshal(xmlResource, strw);
		} catch (JAXBException e) {
			throw new EdalPublicationMetaDataException(
					"Unable to marshall meta data from PublicReference", e);
		}
		return strw.getBuffer();
	}

	/**
	 * Get a new DOI from the DataCite server.
	 * 
	 * @param year
	 *            the current year.
	 * @return the new DOI.
	 * @throws EdalException
	 *             if unable to load a new DOI.
	 */
	private String getPersistentIdentifier(int year) throws EdalException {
		try {
			return new DataCiteSearchConnector().generateNewDOI(year);
		} catch (DataCiteException e) {
			throw new EdalException("unable to load persistent identifier", e);
		}
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * no implementation for DataCite necessary -> impossible to reserve IDs
	 */
	@Override
	public void rejectApprovalRequest(PublicReference publicReference)
			throws EdalApprovalException {
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * Check the
	 * {@link de.ipk_gatersleben.bit.bi.edal.primary_data.metadata.MetaData}
	 * schema against the {@link DataCiteXmlMapper} schema.
	 * 
	 * @throws EdalException
	 *             if validation failed.
	 * 
	 */
	@Override
	public void validateMetaData(PrimaryDataEntityVersion entityVersion)
			throws EdalPublicationMetaDataException {

		DataCiteXmlMapper mapper = new DataCiteXmlMapper(entityVersion);
		XmlResource resource = mapper.createXmlResource();
		mapper.validateSchema(resource);

	}

	@Override
	public void validate(PrimaryDataEntityVersion entityVersion)
			throws EdalPublicationMetaDataException {
		// TODO Auto-generated method stub

	}
}